#if !defined(AD_LOGGER_H_)
#define AD_LOGGER_H_
#include <log4cxx/logger.h>

#include <cassert>
#include <mutex>

namespace autodrive {
namespace perception {
/// @brief autodrive logger
class ADLogger final {
 public:
  ADLogger(ADLogger &&) = delete;
  ADLogger(const ADLogger &) = delete;
  ADLogger &operator=(ADLogger &&) = delete;
  ADLogger &operator=(const ADLogger &) = delete;
  ~ADLogger();

 protected:
  ADLogger();

 public:
  /// @brief init
  /// @param loggerName  logger name
  /// @param err error message
  /// @return false on error
  void Init(std::string const &loggerName) noexcept(false);

  /// @brief get init flag
  /// @return init flag
  bool IsInit() const noexcept { return is_init_; }

  /// @brief get logger
  /// @return logger
  log4cxx::LoggerPtr GetLogger() const noexcept { return logger_; }

  /// @brief write debug log
  /// @param message log message
  void Debug(std::string const &message) noexcept {
    assert(is_init_);
    logger_->debug(message);
  }

  /// @brief write trace log
  /// @param message log message
  void Trace(std::string const &message) noexcept {
    assert(is_init_);
    logger_->trace(message);
  }

  /// @brief write info log
  /// @param message log message
  void Info(std::string const &message) noexcept {
    assert(is_init_);
    logger_->info(message);
  }

  /// @brief write warning message
  /// @param message log message
  void Warning(std::string const &message) noexcept {
    assert(is_init_);
    logger_->warn(message);
  }

  /// @brief write error message
  /// @param message log message
  void Error(std::string const &message) noexcept {
    assert(is_init_);
    logger_->error(message);
  }

  /// @brief write fatal message
  /// @param message log message
  void Fatal(std::string const &message) noexcept {
    assert(is_init_);
    logger_->fatal(message);
  }

  ///@brief Get instance
  ///@return ad logger instance
  static ADLogger &GetInstance() noexcept;

 private:
  /// @brief logger
  static log4cxx::LoggerPtr logger_;

  /// @brief init flag
  bool is_init_{false};

  /// @brief  init call once flag
  mutable std::once_flag init_flag_;
};
}  // namespace perception
}  // namespace autodrive

#define ADLOGGER_DEBUG(message)                                     \
  do {                                                              \
    auto &adLogger{autodrive::perception::ADLogger::GetInstance()}; \
    assert(adLogger.IsInit());                                      \
    LOG4CXX_DEBUG(adLogger.GetLogger(), message);                   \
  } while (0)

#define ADLOGGER_TRACE(message)                                     \
  do {                                                              \
    auto &adLogger{autodrive::perception::ADLogger::GetInstance()}; \
    assert(adLogger.IsInit());                                      \
    LOG4CXX_TRACE(adLogger.GetLogger(), message);                   \
  } while (0)

#define ADLOGGER_INFO(message)                                      \
  do {                                                              \
    auto &adLogger{autodrive::perception::ADLogger::GetInstance()}; \
    assert(adLogger.IsInit());                                      \
    LOG4CXX_INFO(adLogger.GetLogger(), message);                    \
  } while (0)

#define ADLOGGER_WARN(message)                                      \
  do {                                                              \
    auto &adLogger{autodrive::perception::ADLogger::GetInstance()}; \
    assert(adLogger.IsInit());                                      \
    LOG4CXX_WARN(adLogger.GetLogger(), message);                    \
  } while (0)

#define ADLOGGER_ERROR(message)                                     \
  do {                                                              \
    auto &adLogger{autodrive::perception::ADLogger::GetInstance()}; \
    assert(adLogger.IsInit());                                      \
    LOG4CXX_ERROR(adLogger.GetLogger(), message);                   \
  } while (0)

#define ADLOGGER_FATAL(message)                                     \
  do {                                                              \
    auto &adLogger{autodrive::perception::ADLogger::GetInstance()}; \
    assert(adLogger.IsInit());                                      \
    LOG4CXX_FATAL(adLogger.GetLogger(), message);                   \
  } while (0)

#endif  // AD_LOGGER_H_
